<?php

namespace app\api\library;

use app\admin\model\user\UserMember;
use app\admin\validate\user\UserMemberValidate;
use support\Request;
use support\utils\Cache;
use support\utils\Date;
use support\utils\Random;
use think\Exception;
use think\facade\Db;

class Auth
{
    protected $requestUri = '';
    protected $_token   = '';
    protected $validate;
    protected $model;
    protected $_error   = '';

    //Token默认有效时长
    protected $keeptime   = 2592000;

    protected $allowFields = ['id', 'nickname', 'mobile', 'avatar', 'score','email','inviteCode','money'];


    protected static $instance = null;

    protected $optionsCache = [
        '_logined' => "_logined",
        '_user' => "user",
    ];


    public function __construct($options = [])
    {
        $this->validate = new UserMemberValidate();
        $this->model = new UserMember();
    }

    /**
     * @param array $options 参数
     * @return Auth
     */
    public static function instance($options = [])
    {
        if (is_null(self::$instance)) {
            self::$instance = new static($options);
        }

        return self::$instance;
    }



    /**
     * 根据Token初始化
     * @param string $token Token
     * @return boolean
     */
    public function init($token)
    {
        $this->_token   = $token;
        if (!$this->getCacheLogined()) {
             $this->setError('请先登录');
             return false;
        }
        if ($this->_error) {
            return false;
        }
        $data = Token::get($token);
        if (!$data) {
            return false;
        }
        $user_id = intval($data['user_id']);
        if ($user_id > 0) {
            $user = $this->model->where("id",$user_id)->find();
            if (!$user) {
                $this->setError('账户不存在');
                return false;
            }
            if ($user['status'] != '1') {
                $this->setError('账户锁定中');
                return false;
            }
            $this->setCacheUser($user);
            $this->setCacheLogined(1);
            return true;
        } else {
            $this->setError('请先登录');
            return false;
        }
    }


    /**
     * 用户登录
     * @param $account
     * @param $password
     * @return boolean
     */
    public function login($account, $password)
    {
        $field = $this->validate->is($account, 'email') ? 'email' : 'mobile';
        $user  = $this->model->where([$field => $account])->find();
        if (!$user) {
            $this->setError('账户错误');
            return false;
        }
        if ($user->status != '1') {
            $this->setError('账户锁定中');
            return false;
        }
        if ($user->password != getEncryptPassword($password, $user->salt)) {
            $this->setError('密码错误');
            return false;
        }
        //直接登录会员
        return $this->direct($user->id);
    }


    /**
     * 注册用户
     * @param string $username 用户名|手机号
     * @param string $password 密码
     * @param string $email    邮箱
     * @param array  $extend   扩展参数
     * @return boolean
     */
    public function register($username, $password, $mobile = '',$email = '', $extend = [])
    {
        // 检测用户名、昵称、邮箱、手机号是否存在
        if ($this->model::getByNickname($username)) {
            $this->setError('Username already exist');
            return false;
        }
         if ($mobile && $this->model::getByMobile($mobile)) {
            $this->setError('Mobile already exist');
            return false;
        }
        if ($email && $this->model::getByEmail($email)) {
            $this->setError('Email already exist');
            return false;
        }


        $ip   = request()->getRealIp($safe_mode=true);
        $time = time();
        // 手机号格式化处理
        $data = [
            'nickname' => $username,
            'password' => $password,
            'email'    => $email,
            'mobile'   => $mobile,
            'score'    => 0,
            'avatar'   => '/default/avatar.png',
        ];
        $params = array_merge($data, [
            'salt'      => Random::alnum(),
            'jointime'  => $time,
            'joinip'    => $ip,
            'logintime' => $time,
            'loginip'   => $ip,
            'prevtime'  => $time,
            'status'    => '1'
        ]);
        $params['password'] = !empty($password)?getEncryptPassword($password, $params['salt']):'';
        $params = array_merge($params, $extend);
        //账号注册时需要开启事务,避免出现垃圾数据
        Db::startTrans();
        try {
            $user = $this->model->create($params);
            //设置Token
            $this->_token = Random::uuid();
            $this->setCacheLogined(1);
            $this->setCacheUser($user);
            Token::set($this->_token, $user->id, $this->keeptime);
            Db::commit();
        } catch (Exception $e) {
            $this->setError($e->getMessage());
            Db::rollback();
            return false;
        }
        return true;
    }


     /**
     * 直接登录账号
     * @param int $user_id
     * @return boolean
     */
    public function direct($user_id)
    {
        $user  = $this->model->where(['id'=>$user_id])->find();
        if ($user) {
            Db::startTrans();
            try {
                /* 不允许同时登陆 */
                if (config('genthink.api_login_unique')) {
                    Token::clear($user_id);
                }
                $ip   = request()->getRealIp($safe_mode=true);
                $time = time();
                //判断连续登录和最大连续登录
                if ($user->logintime < Date::unixtime('day')) {
                    $user->successions    = $user->logintime < Date::unixtime('day', -1) ? 1 : $user->successions + 1;
                    $user->maxsuccessions = max($user->successions, $user->maxsuccessions);
                }

                $user->prevtime = $user->logintime;
                //记录本次登录的IP和时间
                $user->loginip   = $ip;
                $user->logintime = $time;
                //重置登录失败次数
                $user->loginfailure = 0;
                $user->save();
                //缓存用户token
                $this->_token = Random::uuid();
                    // 缓存用户信息
                $this->setCacheUser($user);
                Token::set($this->_token, $user->id, $this->keeptime);
                // 缓存用户状态
                $this->setCacheLogined(1);
                //登录成功的事件 TODO
                Db::commit();
            } catch (Exception $e) {
                Db::rollback();
                $this->setError($e->getMessage());
                return false;
            }
            return $user->id;
        } else {
            $this->setError("会员信息不存在");
            return false;
        }
    }

    /**
     * 退出登录
     * @return bool
     */
    public function logout()
    {
        if (!$this->getCacheLogined()) {
            $this->setError('你还没有登录');
            return false;
        }
        //设置登录标识
        $this->setCacheLogined(0);
        //删除Token
        Token::delete($this->_token);
        //退出成功的事件
        return true;
    }
    
    
    /**
     * 删除会员
     * @return bool
     */
    public function deleteUser($user_id)
    {
        if (!$this->getCacheLogined()) {
            $this->setError('你还没有登录');
            return false;
        }
        //设置登录标识
        $this->setCacheLogined(0);
        //删除Token
        Token::delete($this->_token);
        //删除会员
        $this->model->destroy($user_id);
        //退出成功的事件
        return true;
    }

    /**
     * 获取会员基本信息
     * @return array
     */
    public function getUserinfo()
    {
        $data        = $this->getCacheUser();
        $allowFields = $this->getAllowFields();
        $userinfo    = array_intersect_key($data, array_flip($allowFields));
        $userinfo    = array_merge($userinfo, Token::get($this->_token));
        return $userinfo;
    }

    /**
     * 修改密码
     * @param string $newpassword       新密码
     * @param string $oldpassword       旧密码
     * @param bool   $ignoreoldpassword 忽略旧密码
     * @return boolean
     */
    public function changepwd($newpassword, $oldpassword = '', $ignoreoldpassword = false)
    {
        if (!$this->getCacheLogined()) {
            $this->setError('You are not logged in');
            return false;
        }
        //判断旧密码是否正确
        if ($this->getCacheUser()['password'] == getEncryptPassword($oldpassword, $this->getCacheUser()['salt']) || $ignoreoldpassword) {
            Db::startTrans();
            try {
                $salt = Random::alnum();
                $newpassword = getEncryptPassword($newpassword, $salt);
                $this->model->where('id', $this->getCacheUser()['id'])->update(['loginfailure' => 0, 'password' => $newpassword, 'salt' => $salt]);
                Token::delete($this->_token);
                //修改密码成功的事件
                Db::commit();
            } catch (Exception $e) {
                Db::rollback();
                $this->setError($e->getMessage());
                return false;
            }
            return true;
        } else {
            $this->setError('Password is incorrect');
            return false;
        }
    }


    /**
     * 检测当前控制器和方法是否匹配传递的数组
     * @param $arr
     * @return bool
     */
    public function match($arr = [])
    {
        $arr = is_array($arr) ? $arr : explode(',', $arr);
        if (!$arr) {
            return false;
        }
        $arr = array_map('strtolower', $arr);
        // 是否存在
        if (in_array(strtolower(Request()->action), $arr) || in_array('*', $arr)) {
            return true;
        }
        // 没找到匹配
        return false;
    }


     /**
     * 判断是否登录
     * @return boolean
     */
    public function isLogin()
    {
        if ($this->getCacheLogined()) {
            return true;
        }
        return false;
    }



     /**
     * 获取允许输出的字段
     * @return array
     */
    public function getAllowFields()
    {
        return $this->allowFields;
    }

     /**
     * 设置允许输出的字段
     * @param array $fields
     */
    public function setAllowFields($fields)
    {
        $this->allowFields = $fields;
    }


    /**
     * 获取User模型
     * @return User
     */
    public function getUser()
    {
        return $this->model->where("id",$this->getCacheUser()['id'])->find();
    }

    /**
     * 获取User模型
     * @return User
     */
    public function getUserId()
    {
        return $this->getCacheUser()['id'];
    }

    /**
     * 兼容调用user模型的属性
     * @param string $name
     */
    public function __get($name)
    {
        return $this->getCacheUser() ? $this->getCacheUser()[$name] : null;
    }

    /**
     * 兼容调用user模型的属性
     */
    public function __isset($name)
    {
        return empty($this->getCacheUser()) ? $this->getCacheUser()[$name]: false;
    }



     /**
     * 设置当前请求的URI
     * @param string $uri
     */
    public function setRequestUri($uri)
    {
        $this->requestUri = $uri;
    }

    /**
     * 缓存登录状态
     * @return void
     */
    public function setCacheLogined($value)
    {
        Cache::set($this->optionsCache['_logined'].$this->_token,$value);
    }

     /**
     * 缓存登录状态
     * @return string
     */
    public function getCacheLogined()
    {
        return Cache::get($this->optionsCache['_logined'].$this->_token);
    }


     /**
     * 缓存用户信息
     * @return void
     */
    public function setCacheUser($value)
    {
        Cache::set($this->optionsCache['_user'].$this->_token,$value);
    }

     /**
     * 获取缓存的用户信息
     * @return string
     */
    public function getCacheUser()
    {
        return Cache::get($this->optionsCache['_user'].$this->_token);
    }

    /**
     * 设置错误信息
     *
     * @param string $error 错误信息
     * @return Auth
     */
    public function setError($error)
    {
        $this->_error = $error;
        return $this;
    }

    /**
     * 获取错误信息
     * @return string
     */
    public function getError()
    {
        return $this->_error ? $this->_error : '';
    }


}